#ifndef __MP_MQTT_CLIENT_H__
#define __MP_MQTT_CLIENT_H__

#include "tcp_client.h"
#include "mqtt_packet.h"
#include "mqtt_client_context.h"

#include "mqtt_client_subscribe.h"
#include "mqtt_client_publish.h"

#include <functional>

//MQTT v3 version

namespace Gazella {

typedef std::function<void(MQTTConnackReturnCode, uint8_t)> mqtt_connack_callback_t;

class MqttClient: public TcpClient {
public:
    MqttClient();
    ~MqttClient();

    //init
    int init(std::string host, int port) override;

    void connect(
        std::string client_id,
        mqtt_connack_callback_t connack_callback,
        std::string will_topic = "",
        std::string will_message = "",
        std::string username = "",
        std::string password = "",
        uint16_t keep_alive = 60,
        uint8_t connect_flags = MQTT_CONNECT_CLEAN_SESSION 
    );
    void disconnect();
    int publish(const std::string& topic_name, const std::string& message, uint8_t qos = 0, uint8_t retain = 0);
    int subscribe(const std::string& topic_filter, mqtt_subscribe_callback_t subscribe_callback, uint8_t qos = 0);
    int unsubscribe(const std::string& topic_filter);

    bool broker_connected();

private:
    void __recv_cb(ev::io &w, int revents) override;

    void __connect_callback() override;
    void __disconnect_callback() override;

    void __ping_timer_callback(ev::timer &w, int revents);

    //command
    void __command_connack (MqttPacketWrapper& mqttPacketWrapper);
    void __command_publish (MqttPacketWrapper& mqttPacketWrapper);
    void __command_puback  (MqttPacketWrapper& mqttPacketWrapper);
    void __command_pubrec  (MqttPacketWrapper& mqttPacketWrapper);
    void __command_pubrel  (MqttPacketWrapper& mqttPacketWrapper);
    void __command_pubcomp (MqttPacketWrapper& mqttPacketWrapper);
    void __command_suback  (MqttPacketWrapper& mqttPacketWrapper);
    void __command_unsuback(MqttPacketWrapper& mqttPacketWrapper);
    void __command_pingresp(MqttPacketWrapper& mqttPacketWrapper);

private:
    ev::timer m_ping_timer;
    uint32_t m_ping_timeout;

    uint32_t m_packet_id_num;

    bool m_broker_connected;

    MqttPacketConnect m_mqtt_packet_connect;

    //connect callback
    mqtt_connack_callback_t m_connack_callback = nullptr;

    //subscribe callback list
    MqttClientSubscribe m_mqtt_client_subscribe;

    //publish manager
    MqttClientPublish m_mqtt_client_publish;

    std::shared_ptr<MqttClientContext> m_mqtt_client_context;
};
}

#endif
